package com.dhsc.ftp;

import javax.annotation.PreDestroy;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import com.dhsc.exception.MyException;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, FTPClient.class })
@ConditionalOnProperty(value = "ftp.enabled", havingValue = "true")
@EnableConfigurationProperties(FTPConfiguration.FtpConfigProperties.class)
public class FTPConfiguration
{
	private ObjectPool<FTPClient> pool;

	public FTPConfiguration(FtpConfigProperties props)
	{
		// 默认最大连接数与最大空闲连接数都为8，最小空闲连接数为0
		// 其他未设置属性使用默认值，可根据需要添加相关配置
		GenericObjectPoolConfig<FTPClient> poolConfig = new GenericObjectPoolConfig<>();
		poolConfig.setTestOnBorrow(true);
		poolConfig.setTestOnReturn(true);
		poolConfig.setTestWhileIdle(true);
		poolConfig.setMinEvictableIdleTimeMillis(60000);
		poolConfig.setSoftMinEvictableIdleTimeMillis(50000);
		poolConfig.setTimeBetweenEvictionRunsMillis(30000);
		pool = new GenericObjectPool<>(new FtpClientPooledObjectFactory(props), poolConfig);
		preLoadingFtpClient(props.getInitialSize(), poolConfig.getMaxIdle());
		// 初始化ftp工具类中的ftpClientPool
		FTPUtil.init(pool);
	}
	
	/**
	 * @Title: preLoadingFtpClient   
	 * @Description: 预先加载FTPClient连接到对象池中
	 * @author: wangxuebo
	 * @param :initialSize 初始化连接数
	 * @param :maxIdle 最大空闲连接数      
	 * @return: void
	 */
	private void preLoadingFtpClient(Integer initialSize, int maxIdle)
	{
		if (initialSize == null || initialSize <= 0)
		{
			return;
		}

		int size = Math.min(initialSize.intValue(), maxIdle);
		for (int i = 0; i < size; i++)
		{
			try
			{
				pool.addObject();
			}
			catch (Exception e)
			{
				log.error("preLoadingFtpClient error...", e);
			}
		}
	}

	@PreDestroy
	public void destroy()
	{
		if (pool != null)
		{
			pool.close();
		}
	}

	/**
	 * Ftp配置属性类，建立ftpClient时使用
	 */
	@Data
	@ConfigurationProperties(prefix = "ftp")
	static class FtpConfigProperties
	{

		private String host = "localhost";

		private int port = FTP.DEFAULT_PORT;

		private String username;

		private String password;

		private int bufferSize = 8096;

		/**
		 * 初始化连接数
		 */
		private Integer initialSize = 0;

	}

	/**
	 * FtpClient对象工厂类
	 */
	static class FtpClientPooledObjectFactory implements PooledObjectFactory<FTPClient>
	{

		private FtpConfigProperties props;

		public FtpClientPooledObjectFactory(FtpConfigProperties props)
		{
			this.props = props;
		}

		@Override
		public PooledObject<FTPClient> makeObject() throws Exception
		{
			FTPClient ftpClient = new FTPClient();
			try
			{
				ftpClient.connect(props.getHost(), props.getPort());
				ftpClient.login(props.getUsername(), props.getPassword());
				ftpClient.setBufferSize(props.getBufferSize());
				ftpClient.setControlEncoding("utf-8");
				ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
				ftpClient.enterLocalPassiveMode();
				return new DefaultPooledObject<>(ftpClient);
			}
			catch (Exception e)
			{
				log.error("建立FTP连接失败", e);
				if (ftpClient.isAvailable())
				{
					ftpClient.disconnect();
				}
				throw new MyException("建立FTP连接失败", e);
			}
		}

		@Override
		public void destroyObject(PooledObject<FTPClient> p) throws Exception
		{
			FTPClient ftpClient = getObject(p);
			if (ftpClient != null && ftpClient.isConnected())
			{
				ftpClient.disconnect();
			}
		}

		@Override
		public boolean validateObject(PooledObject<FTPClient> p)
		{
			FTPClient ftpClient = getObject(p);
			if (ftpClient == null || !ftpClient.isConnected())
			{
				return false;
			}
			try
			{
				ftpClient.changeWorkingDirectory("/");
				return true;
			}
			catch (Exception e)
			{
				log.error("验证FTP连接失败::{}", e);
				return false;
			}
		}

		@Override
		public void activateObject(PooledObject<FTPClient> p) throws Exception
		{
		}

		@Override
		public void passivateObject(PooledObject<FTPClient> p) throws Exception
		{
		}

		private FTPClient getObject(PooledObject<FTPClient> p)
		{
			if (p == null || p.getObject() == null)
			{
				return null;
			}
			return p.getObject();
		}

	}
}
